/*
 * @Author: mikey.zhaopeng 
 * @Date: 2020-02-26 12:57:15 
 * @Last Modified by: mikey.zhaopeng
 * @Last Modified time: 2020-02-28 13:32:14
 * 
 * modify Game.ts for test git rebase command
 */
import { GameManager } from "./managers/GameManager";
import ResourceManager from "./managers/ResourceManager";
import Hero from "./Hero";
import AudioManager from "./managers/AudioManager";
import CommandHandler from "./commands/CommandHandler";
import { Action } from "./commands/Action";
import MoveCommand from "./commands/MoveCommand";
const {ccclass, property} = cc._decorator;
interface LevelData {
    'source': string,
    'row': number[],
    'col': number[]
}
enum GameState {
    INIT,
    BEGAN,
    END
}
export enum Direction {
    TOP,
    BOTTOM,
    LEFT,
    RIGHT
}
interface TouchInfo {
    row: number[],
    col: number[],
    target: cc.Node
}
@ccclass
export default class Game extends cc.Component {

    // onLoad () {}
    @property({type: cc.Node})
    gameCon: cc.Node = null;
    @property({type: cc.Node})
    overNode: cc.Node = null;
    @property({type: cc.AudioClip})
    winAudio: cc.AudioClip = null;
    @property({type: cc.AudioClip})
    backAudio: cc.AudioClip = null;
    @property({type: cc.AudioClip})
    tapAudio: cc.AudioClip = null;

    private map: number[][] = [];
    private row: number = 7;
    private col: number = 6;
    // private 
    private nullRowArr: number[];
    private nullColArr: number[];

    private gridWidth: number = 87;
    private gridHeight: number = 87;

    private levelData: any = null;
    private pointMap: cc.Vec2[][]; 
    // 英雄初始位置列表
    private heroInitPos: cc.Vec2[];
    // 当前关卡的配置数组
    private curConfigData: LevelData[];
    // 节点池
    private heroPool: cc.NodePool = null;

    // 存放英雄的数组
    private heroArr: cc.Node[] = null;

    private _curState: GameState = GameState.INIT;

    // 当前移动的节点
    private currentMoveNode: cc.Node = null;
    // 当前节点的移动方向
    private nodeDirection: Direction = Direction.RIGHT;
    private startPoint: cc.Vec2 = new cc.Vec2(0,0);

    // 当前移动的英雄所能走的方向
    private canMoveDirection: Direction = -1;

    private canMove: boolean = true;
    
    // 曹操所在数组中位置的索引
    private ccIndex: number = -1;
    // 出口数据
    private outRowArr: number[] = [];
    private outColArr: number[] = [];

    private commandHandler: CommandHandler = null;
    async start () {
       this.commandHandler = new CommandHandler();
       // 设置有限状态机状态
       this.curState = GameState.INIT;
       this.nodeDirection = Direction.RIGHT;

       await this.init();
    }
    private btnEvent(e: cc.Event,data: any): void {
        switch(data) {
            case "next":
                break;
            case "pre":
                break;
            case "restart":
                this.restart();
                break;  
            case "undo":
                // 执行悔棋逻辑
                this.commandHandler.handle(Action.UNDO);
                break;      
        }
    }
    private nextLevel(): void {

    }
    private preLevel(): void {

    }
    private restart(): void {
        this.overNode.active = false;
        for(let i = 0; i < this.heroArr.length; i++) {
            let heroItem: cc.Node = this.heroArr[i];
            heroItem.x = this.heroInitPos[i].x;
            heroItem.y = this.heroInitPos[i].y;

            let heroCom: Hero = <Hero>heroItem.getComponent("Hero");
            heroCom.setRowArr(this.curConfigData[i].row);
            heroCom.setColArr(this.curConfigData[i].col);
        }
        this.initMap();
    }
    set curState(state: GameState) {
        switch(state) {
            case GameState.INIT:
                this.curConfigData = [];
                this._curState = GameState.INIT;
                break;
            case GameState.BEGAN:
                this._curState = GameState.BEGAN;
                break;
            case GameState.END:
                this._curState = GameState.END;
                this.gameover();
                break;        
        }
    }
    private gameover(): void {
        this.overNode.active = true;
        this.overNode.getComponent(cc.Animation).play();
        // this.overNode.getChildByName("gameover").getComponent(cc.Animation).play();
        this.overNode.on("touchstart",this.closeMask,this);
    }
    private closeMask(): void {
        let self = this;
        self.overNode.active = false;
        // AudioManager.getInstance().playBg("music/music.mp3",true,1);

    }
    set curDir(dir: Direction) {
        if(this.currentMoveNode) {
            switch(dir) {
                case Direction.TOP:
                    this.nodeDirection = Direction.TOP;
                    this.commandHandler.handle(Action.EXEC,new MoveCommand(this.currentMoveNode,this.map,Direction.TOP,this.pointMap));
                    break;
                case Direction.BOTTOM:
                    this.nodeDirection = Direction.BOTTOM;
                    this.commandHandler.handle(Action.EXEC,new MoveCommand(this.currentMoveNode,this.map,Direction.BOTTOM,this.pointMap));
                    break;    
                case Direction.LEFT:
                    this.nodeDirection = Direction.LEFT;
                    this.commandHandler.handle(Action.EXEC,new MoveCommand(this.currentMoveNode,this.map,Direction.LEFT,this.pointMap));
                    break;
                case Direction.RIGHT:
                    this.nodeDirection = Direction.RIGHT;
                    this.commandHandler.handle(Action.EXEC,new MoveCommand(this.currentMoveNode,this.map,Direction.RIGHT,this.pointMap));
                    break;        
            }
        }
    }
    private canTop(): boolean { 
        let nodeCom: Hero = <Hero>this.currentMoveNode.getComponent("Hero");
        let rowArr: number[] = nodeCom.getRowArr();
        let colArr: number[] = nodeCom.getColArr();
        let maxRow: number = Math.max(...rowArr);
        let rightNum = 0;
        // 向左寻找这些行和左边的列地图是否为0
        for(let i = maxRow + 1; i < maxRow + 2; i++) {
            for(let j = 0; j < colArr.length; j++) {
                if(!this.map[i][colArr[j]]) {
                    rightNum++;
                }
            }
        }
        if(rightNum >= colArr.length) {
            return true;
        } else {
            return false;
        }
    }
    private canBottom(): boolean {
        let nodeCom: Hero = <Hero>this.currentMoveNode.getComponent("Hero");
        let rowArr: number[] = nodeCom.getRowArr();
        let colArr: number[] = nodeCom.getColArr();
        let minRow: number = Math.min(...rowArr);
        let rightNum = 0;
        // 向左寻找这些行和左边的列地图是否为0
        for(let i = minRow - 1; i >= minRow - 1; i--) {
            for(let j = 0; j < colArr.length; j++) {
                if(!this.map[i][colArr[j]]) {
                    rightNum++;
                }
            }
        }
        if(rightNum >= colArr.length) {
            return true;
        } else {
            return false;
        }
    }
    private canLeft(): boolean {
        let nodeCom: Hero = <Hero>this.currentMoveNode.getComponent("Hero");
        let rowArr: number[] = nodeCom.getRowArr();
        let colArr: number[] = nodeCom.getColArr();
        let minCol: number = Math.min(...colArr);
        let rightNum = 0;
        // 向左寻找这些行和左边的列地图是否为0
        for(let i = 0; i < rowArr.length; i++) {
            for(let j = minCol - 1; j >= minCol - 1; j--) {
                if(!this.map[rowArr[i]][j]) {
                    rightNum++;
                }
            }
        }
        if(rightNum >= rowArr.length) {
            return true;
        } else {
            return false;
        }
    }
    private canRight(): boolean {
        let nodeCom: Hero = <Hero>this.currentMoveNode.getComponent("Hero");
        let rowArr: number[] = nodeCom.getRowArr();
        let colArr: number[] = nodeCom.getColArr();
        // 获取最大的列
        let maxCol: number = Math.max(...colArr);
        let rightNum = 0;
        // 向右寻找这些行和右边的列地图是否为0
        for(let i = 0; i < rowArr.length; i++) {
            for(let j = maxCol + 1; j < maxCol + 2; j++) {
                if(!this.map[rowArr[i]][j]) {
                    rightNum++;
                }
            }
        }
        if(rightNum >= rowArr.length) {
            return true;
        } else {
            return false;
        }
    }
    private async init(): Promise<any> {
        // 播放背景音乐
        // AudioManager.getInstance().playBg("music/music.mp3",true,1);
        cc.audioEngine.play(this.backAudio,true,1);
        this.overNode.active = false;
        this.heroArr = [];
        this.heroInitPos = [];
        // 初始化对象池
        this.heroPool = new cc.NodePool();
        this.initNodePool();
        let res: cc.JsonAsset = await this.loadConfigFile("config/level1.json");
        this.levelData = res.json;
        this.initnullRowCol();
        this.initMap();
        await this.initHero();
        // 注册监听事件
        this.gameCon.on('touchstart',this.tapStart,this);
        this.gameCon.on('touchmove',this.tapMove,this);
        this.gameCon.on('touchend',this.tapEnd,this);
    }
    private tapStart(e: cc.Event.EventTouch): void {
        let stageX = e.getLocation().x;
        let stageY = e.getLocation().y;
        this.startPoint = e.getLocation();
        let touchInfo: TouchInfo = this.getRowColArr(stageX,stageY);
        if(touchInfo.target) {
            this.currentMoveNode = touchInfo.target;  
        }
    }
    private tapMove(e: cc.Event.EventTouch): void {
        let stageX = e.getLocation().x;
        let stageY = e.getLocation().y;
        let moveInfo: cc.Vec2 = this.getRowAndCol(stageX,stageY);
        let offsetX: number = stageX - this.startPoint.x;
        let offsetY: number = stageY - this.startPoint.y;
        // 判断移动方向
        if(this.currentMoveNode) {
            // 将当前位置的map设置为0，这样就可以随意的移动了
            let hero: Hero = <Hero>this.currentMoveNode.getComponent("Hero");
            let rowArr: number[] = hero.getRowArr();
            let colArr: number[] = hero.getColArr();
            let sourceX: number = this.getXYByRowArrAndColArr(rowArr,colArr).x;
            if(offsetX > 0 && this.canRight()) {
                // 向右
                if(this.currentMoveNode.x - sourceX >= this.gridWidth) {
                    this.currentMoveNode.x = this.gridWidth + sourceX;
                    this.canMove = false
                } else {
                    this.canMove = true;
                }
                this.canMoveDirection = Direction.RIGHT;
            } else if(offsetX < 0 && this.canRight()) {
                console.log("offsetX is ",offsetX);
                console.log(">>> 可以走but now is <<<");
                if(this.currentMoveNode.x <= sourceX) {
                    this.currentMoveNode.x = sourceX;
                }
            }
            if(offsetX < 0 && this.canLeft()) {
                // 向左
                // let sourceX: number = this.getXYByRowArrAndColArr(rowArr,colArr).x;
                if(sourceX - this.currentMoveNode.x >= this.gridWidth) {
                    this.currentMoveNode.x = sourceX - this.gridWidth;
                    this.canMove = false;
                } else {
                    this.canMove = true;
                }
                this.canMoveDirection = Direction.LEFT;
            } else if(offsetX > 0 && this.canLeft()) {
                console.log("offsetX is ",offsetX);
                console.log("<<<<","but now is right >>>");
                if(this.currentMoveNode.x >= sourceX) {
                    this.currentMoveNode.x = sourceX;
                }
            }
            if(offsetY > 0 && this.canTop()) {
                let maxRow = Math.max(...rowArr);
                let sourceY: number = this.getXYByRowArrAndColArr(rowArr,colArr).y;
                // 向上
                // let maxY: number = boxInfo.maxY;
                if(this.currentMoveNode.y - sourceY >= this.gridHeight) {
                    this.canMove = false;
                    this.currentMoveNode.y = this.gridHeight + sourceY;
                    // this.canMoveDirection = -1;
                } else {
                    this.canMove = true;
                    this.canMoveDirection = Direction.TOP;
                }
            }
            if(offsetY < 0 && this.canBottom()) {
                let minRow = Math.min(...rowArr);
                let sourceY: number = this.getXYByRowArrAndColArr(rowArr,colArr).y;
                // 向下
                // let maxY: number = boxInfo.maxY;
                if(sourceY - this.currentMoveNode.y >= this.gridHeight) {
                    this.canMove = false;
                    // this.canMoveDirection = -1;
                    // this.canMoveDirection
                    this.currentMoveNode.y = sourceY - this.gridHeight;
                } else {
                    this.canMove = true;
                    this.canMoveDirection = Direction.BOTTOM;
                }
            }
            if(this.canMove) {
                if(this.canMoveDirection === Direction.LEFT || this.canMoveDirection === Direction.RIGHT) {
                    this.currentMoveNode.x += offsetX;
                } else if(this.canMoveDirection !== -1){
                    this.currentMoveNode.y += offsetY;
                }
            }
        }
        this.startPoint = e.getLocation();
    }
    // 获得行列
    private getRowAndCol(x: number,y: number): cc.Vec2 {
        let local = this.gameCon.convertToNodeSpace(cc.v2(x,y));
        let col = Math.floor(Math.round((local.x - this.gridWidth / 2) / this.gridWidth));
        let row = Math.floor(Math.round((local.y - this.gridHeight / 2) / this.gridHeight));
        
        return new cc.Vec2(row,col);
    }
    private tapEnd(e: cc.Event.EventTouch): void {
        switch(this.canMoveDirection) {
            case Direction.LEFT:
                this.curDir = Direction.LEFT;
                break;
            case Direction.RIGHT:
                this.curDir = Direction.RIGHT;
                break;
            case Direction.TOP:
                this.curDir = Direction.TOP;
                break;
            case Direction.BOTTOM:
                this.curDir = Direction.BOTTOM;
                break;            
        }
        // AudioManager.getInstance().playEffect("music/sound");
        if(this.canMoveDirection !== -1) {
            cc.audioEngine.play(this.tapAudio,false,1);
        }
        this.canMoveDirection = -1;
        // this.tempMoveDirection = -1;
        this.checkCCIsOut();
        this.checkCCOuted();
    }
    // 检查曹操是否在出口
    private checkCCIsOut(): void {
        let res: boolean = false;
        if(this.ccIndex !== -1 && this.heroArr[this.ccIndex].name === "cc") {
            let ccNode: cc.Node = this.heroArr[this.ccIndex];
            let heroCom: Hero = <Hero>ccNode.getComponent("Hero");
            let includeOneTwoRow: boolean = heroCom.getRowArr().indexOf(1) >= 0 && heroCom.getRowArr().indexOf(2) >= 0;
            let includeTwoThreeCol: boolean = heroCom.getColArr().indexOf(2) >= 0 && heroCom.getColArr().indexOf(3) >= 0;
            if(includeTwoThreeCol && includeOneTwoRow) {
                for(let m = 0; m < this.outRowArr.length; m++) {
                    for(let n = 0; n < this.outColArr.length; n++) {
                        this.map[this.outRowArr[m]][this.outColArr[n]] = 0;
                    }
                }
            } else {
                for(let m = 0; m < this.outRowArr.length; m++) {
                    for(let n = 0; n < this.outColArr.length; n++) {
                        this.map[this.outRowArr[m]][this.outColArr[n]] = 1;
                    }
                }
            }
        }
    }
    // 检查曹操是否已经出去了
    private checkCCOuted(): void {
        // let res: boolean = false;
        if(this.ccIndex !== -1 && this.heroArr[this.ccIndex].name === "cc") {
            let ccNode: cc.Node = this.heroArr[this.ccIndex];
            let heroCom: Hero = <Hero>ccNode.getComponent("Hero");
            let includeOneTwoRow: boolean = heroCom.getRowArr().indexOf(0) >= 0 && heroCom.getRowArr().indexOf(1) >= 0;
            let includeTwoThreeCol: boolean = heroCom.getColArr().indexOf(2) >= 0 && heroCom.getColArr().indexOf(3) >= 0;
            if(includeTwoThreeCol && includeOneTwoRow) {
                // AudioManager.getInstance().stopBg();
                // AudioManager.getInstance().playEffect("music/laugh");
                // cc.audioEngine.stop();
                cc.audioEngine.play(this.winAudio,false,1);
                this.curState = GameState.END;
            }
        }
    }
    // 点击获得英雄的行和列
    private getRowColArr(x: number,y: number): TouchInfo {
        let res = {
            row: [],
            col: [],
            target: null
        }
        for(let i = 0; i < this.heroArr.length; i++) {
            if(this.heroArr[i].getBoundingBoxToWorld().contains(cc.v2(x,y))) {
                let hero: Hero = <Hero>this.heroArr[i].getComponent("Hero");
                res.row = hero.getRowArr();
                res.col = hero.getColArr();
                res.target = this.heroArr[i];
                return res;
            }
        }
        return res;
    } 
    private initNodePool(): void {
        for(let i = 0; i < 12; i++) {
            let node: cc.Node = new cc.Node();
            node.addComponent(cc.Sprite);
            node.addComponent("Hero");
            this.heroPool.put(node);
        }
    }
    private loadConfigFile(configFile: string): Promise<cc.JsonAsset> {
        return new Promise((resolve,reject) => {
            cc.loader.loadRes(configFile,cc.JsonAsset,(err: Error,res: any) => {
                if(err) {
                    reject();
                }
                console.log("res is ",res);
                resolve(res);
            });
        })
    }
    private initArr(): void {
        this.map = [];
        this.pointMap = [];
        
    }
    private async initHero(): Promise<any> {
        let level = GameManager.getInstance().curLevel;
        let data: Array<LevelData> = this.levelData[level];
        for(let i = 0; i < data.length; i++) {
            let dataItem: LevelData = data[i];
            if(dataItem.source === "" || dataItem.source === "out") {
                
            } else {
                let sumX: number = 0;
                let sumY: number = 0;
                for(let m = 0; m < dataItem.col.length; m++) {
                    for(let n = 0; n < dataItem.row.length; n++) {
                        let positionItem: cc.Vec2 = this.pointMap[dataItem.row[n]][dataItem.col[m]];
                        if(positionItem) {
                            sumX += positionItem.x;
                            sumY += positionItem.y;
                        }
                    }
                }
                if(sumY !== 0 && sumX !== 0) {
                    let max = dataItem.row.length * dataItem.col.length;
                    this.curConfigData.push(dataItem);
                    // 初始化英雄的位置
                    let x: number = sumX / max;
                    let y: number = sumY / max;
                    // 图片的资源加载
                    let nodeItem: cc.Node = this.heroPool.get();
                    nodeItem.getComponent("Hero").setRowArr(dataItem.row);
                    nodeItem.getComponent("Hero").setColArr(dataItem.col);
                    // nodeItem.on('touchstart',)
                    // console.log("start time is ",new Date().getMilliseconds());
                    let startTime: number = new Date().getMilliseconds();
                    let frame: cc.SpriteFrame = await ResourceManager.getInstance().loadResourceByUrl("imgs/" + dataItem.source,cc.SpriteFrame);
                    console.log("加载预制体中的精灵用时：",new Date().getMilliseconds() - startTime);
                    let tex: cc.Texture2D = frame.getTexture();
                    nodeItem.getComponent(cc.Sprite).spriteFrame = frame;
                    nodeItem.name = dataItem.source;
                    nodeItem.width = tex.width;
                    nodeItem.height = tex.height;
                    this.heroInitPos.push(new cc.Vec2(x,y));
                    nodeItem.setPosition(cc.v2(x,y));
                    if(nodeItem.name === "cc") {
                        this.ccIndex = this.heroArr.length;
                    }
                    this.heroArr.push(nodeItem);
                    nodeItem.parent = this.gameCon;

                    
                }
            }
        }
    }
    private getXYByRowArrAndColArr(rowArr: number[],colArr: number[]): cc.Vec2 {
        let sumX: number = 0;
        let sumY: number = 0;
        for(let m = 0; m < colArr.length; m++) {
            for(let n = 0; n < rowArr.length; n++) {
                let positionItem: cc.Vec2 = this.pointMap[rowArr[n]][colArr[m]];
                if(positionItem) {
                    sumX += positionItem.x;
                    sumY += positionItem.y;
                }
            }
        }
        let res: cc.Vec2 = new cc.Vec2(0,0);
        let max = rowArr.length * colArr.length;
        res.x = sumX / max;
        res.y = sumY / max;
        return res;
    }
    private initnullRowCol(): void {
        let level = GameManager.getInstance().curLevel;
        let data: Array<LevelData> = this.levelData[level];
        
        for(let i = 0; i < data.length; i++) {
            let dataItem: LevelData = data[i];
            if(dataItem.source === "") {
                this.nullRowArr = dataItem.row;
                this.nullColArr = dataItem.col;
                // return;
            }
            if(dataItem.source === "out") {
                this.outRowArr = dataItem.row;
                this.outColArr = dataItem.col;
            }
        }
    }
    private initMap(): void {
        this.initArr();
        for(let i = 0; i < this.row; i++) {
            this.map[i] = [];
            this.pointMap[i] = [];
            for(let j = 0; j < this.col; j++) {
                
                if(this.nullRowArr.indexOf(i) >= 0 && this.nullColArr.indexOf(j) >= 0) {
                    this.map[i][j] = 0;
                } else {
                    // if(this.outRowArr.indexOf(i) >= 0 && this.outColArr.indexOf(j) >= 0) {
                    //     this.map[i][j] = 0;
                    // } else {
                        this.map[i][j] = 1;
                    // }
                }
                this.pointMap[i][j] = new cc.Vec2(j * this.gridWidth + this.gridWidth / 2,i * this.gridHeight + this.gridHeight / 2);
            }
        }
    }
    onDestroy(): void {
        // 注销监听事件
        this.gameCon.off('touchstart',this.tapStart,this);
        this.gameCon.off('touchmove',this.tapMove,this);
        this.gameCon.off('touchend',this.tapEnd,this);
    }
    update (dt) {

    }
}
